using Microsoft.Xna.Framework;
using Rocuna.Graphics.Constants;
using Rocuna.Graphics.Core;

namespace WindowsGameLibrary1.Cameras
{
    /// <summary>
    /// Chase camera (Following object).
    /// </summary>
    public class ChaseCamera : Camera
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ChaseCamera"/> class.
        /// </summary>
        /// <param name="positionOffset">The position offset.</param>
        /// <param name="targetOffset">The target offset.</param>
        /// <param name="relativeCameraRotation">The relative camera rotation.</param>
        /// <param name="game">The game.</param>
        public ChaseCamera(Vector3 positionOffset, Vector3 targetOffset, Vector3 relativeCameraRotation, Game game)
            : base(game)
        {
            PositionOffset = positionOffset;
            TargetOffset = targetOffset;
            RelativeCameraRotation = relativeCameraRotation;
        }

        #region Properties

        /// <summary>
        /// Gets or sets the target.
        /// </summary>
        /// <value>
        /// The target.
        /// </value>
        public Vector3 Target { get; set; }

        /// <summary>
        /// Gets or sets the follow target position.
        /// </summary>
        /// <value>
        /// The follow target position.
        /// </value>
        public Vector3 FollowTargetPosition { get; set; }

        /// <summary>
        /// Gets or sets the floow target rotation.
        /// </summary>
        /// <value>
        /// The floow target rotation.
        /// </value>
        public Vector3 FollowTargetRotation { get; set; }

        /// <summary>
        /// Gets or sets the position offset.
        /// </summary>
        /// <value>
        /// The position offset.
        /// </value>
        public Vector3 PositionOffset { get; set; }

        /// <summary>
        /// Gets or sets the target offset.
        /// </summary>
        /// <value>
        /// The target offset.
        /// </value>
        public Vector3 TargetOffset { get; set; }

        /// <summary>
        /// Gets or sets the relative camera rotation.
        /// </summary>
        /// <value>
        /// The relative camera rotation.
        /// </value>
        public Vector3 RelativeCameraRotation { get; set; }

        /// <summary>
        /// Gets or sets the springiness.
        /// </summary>
        /// <value>
        /// The springiness.
        /// </value>
        public float Springiness
        {
            get { return _springiness; }
            set { _springiness = MathHelper.Clamp(value, 0, 1); }
        }

        #endregion

        #region Fields

        private float _springiness = .15f;

        #endregion

        #region Overrides of Camera

        /// <summary>
        /// Gets the name.
        /// </summary>
        public override string Name
        {
            get { return CamerasNames.ChaseCamera; }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Moves the specified new follow target position.
        /// </summary>
        /// <param name="newFollowTargetPosition">The new follow target position.</param>
        /// <param name="newFollowTargetRotation">The new follow target rotation.</param>
        public void Move(Vector3 newFollowTargetPosition, Vector3 newFollowTargetRotation)
        {
            FollowTargetPosition = newFollowTargetPosition;
            FollowTargetRotation = newFollowTargetRotation;
        }

        /// <summary>
        /// Rotates the specified rotation change.
        /// </summary>
        /// <param name="rotationChange">The rotation change.</param>
        public void Rotate(Vector3 rotationChange)
        {
            RelativeCameraRotation += rotationChange;
        }

        /// <summary>
        /// Called when the GameComponent needs to be updated. Override this method with component-specific update code.
        /// </summary>
        /// <param name="gameTime">Time elapsed since the last call to Update</param>
        public override void Update(GameTime gameTime)
        {
            var combinedRotation = FollowTargetRotation + RelativeCameraRotation;
            var rotation = Matrix.CreateFromYawPitchRoll(combinedRotation.Y, combinedRotation.X, combinedRotation.Z);
            var desiredPosition = FollowTargetPosition + Vector3.Transform(PositionOffset, rotation);

            Position = Vector3.Lerp(Position, desiredPosition, Springiness);
            Target = FollowTargetPosition + Vector3.Transform(TargetOffset, rotation);

            var up = Vector3.Transform(Vector3.Up, rotation);

            View = Matrix.CreateLookAt(Position, Target, up);
            base.Update(gameTime);
        }

        #endregion
    }
}